﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace CompilerForC.SyntaxAnalysis
{
    using LexicalAnalysis;

    /// <summary>
    /// Exception thrown if invalid operation is encountered during token stream
    /// processing.
    /// </summary>
    /// <seealso cref="TokenStream"/>
    class TokenStreamException : Exception
    {
        #region Constructors

        public TokenStreamException() : base()
        {
        }

        public TokenStreamException(string message) : base(message)
        {
        }

        public TokenStreamException(string message, Exception innerException) : base(message, innerException)
        {
        }

        #endregion Constructors
    }

    /// <summary>
    /// Class used to encapsulate common operations upon stream of tokens.
    /// </summary>
    /// <seealso cref="Parser"/>
    [DebuggerDisplayAttribute("CurrentIndex = {_currentIndex}")]
    class TokenStream : ReadOnlyCollection<Token>
    {
        #region Private Fields

        /// <summary>
        /// Current token stream index.
        /// </summary>
        private int _currentIndex;
        /// <summary>
        /// Data tables received from the lexical analysis.
        /// </summary>
        private LexerTables _lexerTables;

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Returns true if current token stream index can be advanced.
        /// </summary>
        public bool CanAdvance
        {
            get { return _currentIndex >= 0 && _currentIndex < Count; }
        }

        /// <summary>
        /// Gets or sets current token stream index.
        /// </summary>
        /// <exception cref="TokenStreamException">Thrown if index is out of the token stream range.</exception>
        public int CurrentIndex
        {
            get { return _currentIndex; }
            set
            {
                if (value < 0 || value > Count)
                    throw new TokenStreamException("Current token stream index is out of the token list range.");                

                _currentIndex = value;
            }
        }

        /// <summary>
        /// Gets token at the CurrentIndex index.
        /// </summary>
        /// <exception cref="TokenStreamException">Thrown if current index is out of the token stream range.</exception>
        public Token CurrentToken
        {
            get
            {
                if (!CanAdvance)
                    throw new TokenStreamException("Invalid current token stream index.");

                return this[_currentIndex];
            }
        }

        /// <summary>
        /// Gets data tables received from the lexical analysis.
        /// </summary>
        public LexerTables Tables
        {
            get { return _lexerTables; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Constructs TokenStream. Current token index is set to zero.
        /// </summary>
        /// <param name="lexerTables">Tables filled by lexical analyzer.</param>
        public TokenStream(LexerTables lexerTables) : this(lexerTables, 0)
        {
        }

        /// <summary>
        /// Constructs TokenStream.
        /// </summary>
        /// <param name="lexerTables">Tables filled by lexical analyzer.</param>
        /// <param name="startIndex">Initial current index value.</param>
        public TokenStream(LexerTables lexerTables, int startIndex) : base(lexerTables.Tokens)
        {
            _lexerTables = lexerTables;
            CurrentIndex = startIndex;
        }

        #endregion Constructors

        #region Public Methods

        /// <summary>
        /// Returns current token and increases current index value by one.
        /// </summary>
        /// <returns>Token.</returns>
        public Token Advance()
        {
            Token token = CurrentToken;

            _currentIndex++;
            return token;
        }

        /// <summary>
        /// Returns array of tokens and increases current index by the specified
        /// value.
        /// </summary>
        /// <param name="count">Number of tokens to advance.</param>
        /// <returns>Tokens.</returns>
        public Token[] Advance(int count)
        {
            Token[] tokens = new Token[count];

            for (int tokenIndex = 0; tokenIndex < count; ++tokenIndex)
                tokens[tokenIndex] = Advance();

            return tokens;
        }

        /// <summary>
        /// Tries to find token of the specified value. Search is performed from
        /// the current token index till the end of the token stream.
        /// </summary>
        /// <param name="tokenValue">Value to search for.</param>
        /// <returns>Token index or -1 if token of specified value could not be found.</returns>
        public int Find(string tokenValue)
        {
            for (int tokenIndex = _currentIndex; tokenIndex < Count; ++tokenIndex)
            {
                if (_lexerTables.GetTokenValue(this[tokenIndex]).Equals(tokenValue))
                    return tokenIndex;
            }

            return -1;
        }

        #endregion Public Methods
    }
}
